﻿using Microsoft.Extensions.Logging;
using System.Net.Sockets;
using System.Net;
using System.Collections.Concurrent;
using System.Drawing;
using System;

namespace TcpJsonService.Core
{
    /// <summary>
    /// 拆包结果
    /// </summary>
    public enum UnpackResult
    {
        /// <summary>
        /// 未分析出协议
        /// </summary>
        Empty,
        /// <summary>
        /// 半包
        /// </summary>
        Half,
        /// <summary>
        /// 全包
        /// </summary>
        Full
    };

    /// <summary>
    /// tcp通道
    /// </summary>
    public class TcpChannel
    {
        /// <summary>
        /// 缓冲容量
        /// </summary>
        private const int BufferLength = 5 * 1024 * 1024;

        /// <summary>
        /// 重连间隔时间(毫秒)
        /// </summary>
        private const int ReconnectSpan = 3000;

        /// <summary>
        /// 套接字
        /// </summary>
        private readonly Socket _socket;

        /// <summary>
        /// 日志接口
        /// </summary>
        protected readonly ILogger _logger;

        /// <summary>
        /// 客户端集合
        /// </summary>
        protected readonly ConcurrentDictionary<Socket, object> _accpetSockets = new();

        /// <summary>
        /// 残包
        /// </summary>
        private readonly List<byte> _residueBuffer = [];

        /// <summary>
        /// 构造函数,服务端
        /// </summary>
        /// <param name="port">本地监听端口</param>
        /// <param name="logger">日志</param>
        public TcpChannel(int port, ILogger logger)
        {
            _logger = logger;

            IPEndPoint localEndPoint = new(IPAddress.Any, port);
            _socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                _socket.Bind(localEndPoint);
                _socket.Listen(10);
            }
            catch (SocketException ex)
            {
                _socket.Close();
                _logger.LogError(ex, $"listen error {localEndPoint}");
                return;
            }
            _logger.LogInformation($"listen {_socket.Handle} {localEndPoint}");

            SocketAsyncEventArgs acceptArgs = new();
            acceptArgs.Completed += AcceptedEventHandler;
            if (!_socket.AcceptAsync(acceptArgs))
            {
                AcceptedEventHandler(this, acceptArgs);
            }
        }

        /// <summary>
        /// 构造函数,客户端
        /// </summary>
        /// <param name="remoteEndPoint">远程连接地址</param>
        /// <param name="logger">日志</param>
        public TcpChannel(IPEndPoint remoteEndPoint, ILogger logger)
        {
            _logger = logger;

            _socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            SocketAsyncEventArgs connectArgs = new()
            {
                RemoteEndPoint = remoteEndPoint
            };
            connectArgs.Completed += ConnectedEventHandler;
            if (!_socket.ConnectAsync(connectArgs))
            {
                ConnectedEventHandler(this, connectArgs);
            }
        }

        /// <summary>
        /// 客户端连入事件函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AcceptedEventHandler(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                _logger.LogInformation($"accept {e.AcceptSocket.Handle} {(IPEndPoint)e.AcceptSocket.LocalEndPoint} {(IPEndPoint)e.AcceptSocket.RemoteEndPoint}");

                _accpetSockets.TryAdd(e.AcceptSocket, new object());
                SocketAsyncEventArgs receiveArgs = new()
                {
                    AcceptSocket = e.AcceptSocket
                };
                receiveArgs.Completed += ReceivedHandler;
                receiveArgs.SetBuffer(new byte[BufferLength], 0, BufferLength);
                if (!e.AcceptSocket.ReceiveAsync(receiveArgs))
                {
                    ReceivedHandler(this, receiveArgs);
                }

                e.AcceptSocket = null;
                if (!_socket.AcceptAsync(e))
                {
                    AcceptedEventHandler(this, e);
                }
            }
            else
            {
                _logger.LogInformation($"unknown accept error {e.SocketError}");
            }
        }

        /// <summary>
        /// 连接到服务端事件函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ConnectedEventHandler(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                _logger.LogInformation($"connect {e.ConnectSocket.Handle} {e.ConnectSocket.RemoteEndPoint}");

                SocketAsyncEventArgs receiveArgs = new()
                {
                    AcceptSocket = e.ConnectSocket,
                    UserToken = e
                };
                receiveArgs.Completed += ReceivedHandler;
                receiveArgs.SetBuffer(new byte[BufferLength], 0, BufferLength);
                if (!e.ConnectSocket.ReceiveAsync(receiveArgs))
                {
                    ReceivedHandler(this, receiveArgs);
                }
            }
            //服务端拒绝
            else if (e.SocketError == SocketError.ConnectionRefused)
            {
                Thread.Sleep(ReconnectSpan);
                if (!_socket.ConnectAsync(e))
                {
                    ConnectedEventHandler(this, e);
                }
            }
            else
            {
                _logger.LogInformation($"unknown connect error {e.SocketError}");
            }
        }

        /// <summary>
        /// 拆包
        /// </summary>
        /// <param name="socket">套接字</param>
        /// <param name="buffer">字节流</param>
        /// <param name="offset">当前偏移</param>
        /// <returns>拆包成功返回true,当前是半包返回false</returns>
        protected virtual bool Unpack(Socket socket, List<byte> buffer, ref int offset)
        {
            offset += buffer.Count;
            return true;
        }

        /// <summary>
        /// 接收事件函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReceivedHandler(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                if (e.BytesTransferred > 0)
                {
                    try
                    {
                        _residueBuffer.AddRange(e.Buffer.Take(e.BytesTransferred));
                        int offset = 0;
                        bool hasHalfPack = false;
                        do
                        {
                            if (!Unpack(e.AcceptSocket, _residueBuffer, ref offset))
                            {
                                hasHalfPack = true;
                                break;
                            }
                        } while (offset < _residueBuffer.Count);
                        if (hasHalfPack)
                        {
                            _residueBuffer.RemoveRange(0, offset);
                        }
                        else
                        {
                            _residueBuffer.Clear();
                        }

                        if (!e.AcceptSocket.ReceiveAsync(e))
                        {
                            ReceivedHandler(this, e);
                        }
                    }
                    catch(Exception ex)
                    {
                        _logger.LogError(ex, $"data: {ByteConvert.ToHex([.. e.Buffer.Take(e.BytesTransferred)])} all:{ByteConvert.ToHex(_residueBuffer)}");
                    }
                }
                //正常断开
                else
                {
                    if (e.UserToken == null)
                    {
                        _logger.LogInformation($"disconnect {e.AcceptSocket.Handle} {e.AcceptSocket.LocalEndPoint} {e.AcceptSocket.RemoteEndPoint}");
                        e.AcceptSocket.Close();
                        _accpetSockets.TryRemove(e.AcceptSocket, out _);
                    }
                    else
                    {
                        _logger.LogInformation($"disconnect {e.AcceptSocket.Handle} {e.AcceptSocket.RemoteEndPoint}");
                        _socket.Disconnect(true);
                        if (!_socket.ConnectAsync((SocketAsyncEventArgs)e.UserToken))
                        {
                            ConnectedEventHandler(this, (SocketAsyncEventArgs)e.UserToken);
                        }
                    }
                }
            }
            //异常断开
            else if (e.SocketError == SocketError.ConnectionReset)
            {
                if (e.UserToken == null)
                {
                    _logger.LogInformation($"reset {e.AcceptSocket.Handle} {e.AcceptSocket.LocalEndPoint} {e.AcceptSocket.RemoteEndPoint}");
                    e.AcceptSocket.Close();
                    _accpetSockets.TryRemove(e.AcceptSocket, out _);
                }
                else
                {
                    _logger.LogInformation($"reset {e.AcceptSocket.Handle} {e.AcceptSocket.RemoteEndPoint}");
                    _socket.Disconnect(true);
                    if (!_socket.ConnectAsync((SocketAsyncEventArgs)e.UserToken))
                    {
                        ConnectedEventHandler(this, (SocketAsyncEventArgs)e.UserToken);
                    }
                }
            }
            else
            {
                _logger.LogInformation($"unknown receive error {e.AcceptSocket.Handle} {e.SocketError}");
            }
        }

        /// <summary>
        /// 向服务端发送字节流
        /// </summary>
        /// <param name="buffer">字节流</param>
        public bool Send(byte[] buffer)
        {
            return Send(_socket, buffer);
        }

        /// <summary>
        /// 向客户端发送字节流
        /// </summary>
        /// <param name="socket">套接字</param>
        /// <param name="buffer">字节流</param>
        public bool Send(Socket socket, byte[] buffer)
        {
            if (!socket.Connected)
            {
                return false;
            }
            try
            {
                int result = socket.Send(buffer);
                if (result == buffer.Length)
                {
                    return true;
                }
                else
                {
                    _logger.LogWarning($"send {socket.Handle} except:{buffer.Length} real:{result}");
                    return false;
                }
            }
            catch (SocketException ex)
            {
                _logger.LogError($"send {socket.Handle} {ex.ErrorCode} {ex.SocketErrorCode}");
                return false;
            }
        }

        /// <summary>
        /// 发送字节流到所有连入的客户端
        /// </summary>
        /// <param name="buffer"></param>
        public void Notice(byte[] buffer)
        {
            foreach (var pair in _accpetSockets)
            {
                Send(pair.Key, buffer);
            }
        }

        /// <summary>
        /// 关闭通道
        /// </summary>
        public virtual void Close()
        {
            foreach (var s in _accpetSockets.Keys)
            {
                _logger.LogInformation($"close {s.Handle} {s.LocalEndPoint} {s.RemoteEndPoint}");
                s.Shutdown(SocketShutdown.Both);
                s.Close();
            }
            _logger.LogInformation($"close {_socket.Handle} {_socket.LocalEndPoint} {_socket.RemoteEndPoint}");
            _socket.Shutdown(SocketShutdown.Both);
            _socket.Close();
        }
    }
}
